# -*- coding: utf-8 -*-

# __date:       2021/6/16
# __author:     YangChao
# __function:   自动化校准实验顶层接口

import os
import random
from typing import List
from datetime import datetime

import numpy as np

from core.log import iqlog
from core.instrument.dc import DC
from core.instrument.dac import DAC
from core.tool.utlis import qarange
from core.tool.plot_data import pic_scatter
from core.tool.parse_yaml import parse_yaml_args
from core.instrument.spectrum_analyzer import SpectrumAnalyzer


class TopCalibration:

    def __init__(self,
                 dc: DC,
                 dac: DAC,
                 analyzer: SpectrumAnalyzer
                 ):

        self.dc = dc
        self.dac = dac
        self.analyzer = analyzer
        self.file = r'D:/iq_aotucalibration'
        self.result = []

        self._config = None

        # 将配置文件中参数加载到本地类中
        self._init_config()
        self._save_file()

    def run(self):
        pass

    def _leak_calibration(self, scopex, stepx, scopey, stepy, choice):
        """
        枚举校准
        Args:
            scopex: x遍历范围
            stepx: 步长
            scopey: y遍历范围
            stepy: 步长
            choice: 判断进行dc校准还是amp-phase优化

        Returns:
            优化后的x， y值
        """
        anno = 'local' if choice else 'sideband'
        param = 'IDC-QDC' if choice else 'Amp-Phase'
        axis_name = param.split("-")
        a, b = self._get_param(choice)
        left_x, right_x = self._validate_param(a, scopex, axis_name[0])
        left_y, right_y = self._validate_param(b, scopey, axis_name[1])
        sweep_x = qarange(left_x, right_x, stepx)
        sweep_y = qarange(left_y, right_y, stepy)
        x, y = np.meshgrid(sweep_x, sweep_y)
        px = x.flatten()
        py = y.flatten()
        leak_list = []
        for i in range(len(px)):
            leak = self._set_param(px[i], py[i], choice)
            leak_list.append(leak)
        index = np.argmax(leak_list)
        # pic_plot(leak_list, f'{anno}_leak_calibration_RF={self.f_rf}', self.file, index)
        data = np.array((px, py, leak_list))
        data_name = f"{anno}_enum_no{self.count}"
        self._save_data(data, data_name)
        pic_scatter(px, py, leak_list, f'{anno} leak calibration RF={self.f_rf} no{self.count}',
                    param, self.data_loc, index)
        self.count += 1
        self._set_param(px[index], py[index], choice)
        print(f'{anno} leak calibration: idc={px[index]} qdc={py[index]} leak={leak_list[index]}')
        print(f"\tcalibration data save in {data_name}.dat, just like ({axis_name[0]} {axis_name[1]} Leak)")
        return [px[index], py[index]]

    def _get_param(self, choice):
        """
        获取DC或者DAC的参数信息
        Args:
            choice: DC/DAC切换标志

        Returns:
            当前硬件参数
        """
        if choice:
            a = self.dc.get_i_dc()
            b = self.dc.get_q_dc()
        else:
            a = self.dac.get_q_amp()
            b = self.dac.get_q_phase()
        return a, b

    def _set_param(self, a, b, choice):
        """
        为DC/DAC设置上指定参数，并获取当前泄露指标
        Args:
            a: 参数一
            b: 参数二
            choice: 标志位

        Returns:
            泄露指标
        """
        if choice:
            self.dc.set_i_dc(a)
            self.dc.set_q_dc(b)
            # return self.analyzer.local_leak()
        else:
            self.dac.set_q_amp(a)
            self.dac.set_q_phase(b)
            # return self.analyzer.sideband_leak()

        return random.random()

    def _init_inst(self, f_rf):
        """
        初始化设备，设置DAC中频，设置DC初始电压，设置DAC初始幅值相位，设置频率分析仪的三个maker点
        Args:
            f_rf: 当前的RF频率

        Returns:

        """
        self.f_rf = f_rf
        self.data_loc = self.file + f"/RF{f_rf}"
        self.count = 0
        f_if = abs(f_rf - self.f_lo)
        self.dac.set_IF(f_if)
        # 设置dc初始值
        self.dc.set_i_dc(0)
        self.dc.set_q_dc(0)
        # 设置初始幅值和相位
        if f_rf > self.f_lo:
            self.dac.set_i_phase(0)
            self.dac.set_q_phase(90)
            f_sb = self.f_lo - f_if
        else:
            self.dac.set_i_phase(90)
            self.dac.set_q_phase(0)
            f_sb = self.f_lo + f_if
        self.dac.set_i_amp(self.init_amp)
        self.dac.set_q_amp(self.init_amp)
        # self.analyzer.config_default(f_rf, self.f_lo, f_sb)

    def _save_result(self):
        """
        保存校准结果到指定文件中
        Returns:

        """
        if self.file is not None:
            if not os.path.exists(self.file):
                os.makedirs(self.file)
            path = self.file + '/RF-LO-Amp-Phase-IDC-QDC.dat'
            np.savetxt(path, np.array(self.result), fmt='%.6f', delimiter='\t')
            print(f'calibration result save in {self.file}')
            iqlog.info(f'calibration result save in {self.file}')
        iqlog.info(f"--- LO:{self.f_lo} RF:{self.sweep_rf[0]}-{self.sweep_rf[-1]} {self.__class__.__name__} end ---\n")

    def _save_data(self, data, anno):
        """
        保存中间优化过程数据，方便查询校准记录
        Args:
            data: 优化过程数据
            anno: 过程说明

        Returns:

        """
        if self.data_loc is not None:
            if not os.path.exists(self.data_loc):
                os.makedirs(self.data_loc)
            path = self.data_loc + f'/{anno}.dat'
            np.savetxt(path, data.T, fmt='%.3f', delimiter='\t\t')

    def _save_file(self):
        """
        设置校准结果存储路径
        Returns:

        """
        stime = datetime.now().strftime("%Y-%m-%d %H.%M.%S")
        path = self.file + f'/{self.__class__.__name__}/{stime}'
        self.file = path

    def _init_config(self):
        """
        初始化yaml文件的配置参数
        Returns:

        """
        data = parse_yaml_args()
        self._config = data
        self.__dict__.update(data)
        if not self.multiple_test:
            self.rf_list = [self.cur_rf]
        else:
            try:
                self.rf_list = qarange(self.sweep_rf[0], self.sweep_rf[1], self.sweep_rf[2])
            except Exception as e:
                print("RF扫描参数输入有误，请输入正确的参数")

    def _validate_param(self, x, scope, name):
        # 校验参数，避免参数越界
        if name == 'IDC' or name == "QDC":
            param_min = self.dc_min
            param_max = self.dc_max
        elif name == 'Amp':
            param_min = self.amp_min
            param_max = self.amp_max
        elif name == 'Phase':
            param_min = self.phase_min
            param_max = self.phase_max
        else:
            raise NameError
        left = max(x - scope, param_min)
        right = min(x + scope, param_max)
        return left, right
